package com.unitd.frame.mybatis.parser;

import com.unitd.frame.comm.utils.StringUtils;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import javax.persistence.Column;
import javax.persistence.Table;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

/**
 * @desc Mybatis生成的对应的model、dao、xml文件属性的定义类
 * 用于Mybatis与这些类及文件之间进行生成与解析之间的映射处理
 * @filename EntityInfo.java
 * @copyright www.unitd.com
 * @author Hudan
 * @version 1.0
 * @date 2017/2/4
 */
public class EntityInfo {

	/** 数据库表名 */
	private String tableName;
	 /** Mybatis对应的model实体对象 */
	private Class<?> entityClass;
	 /** Mybatis对应的mapper数据库操作对象 */
	private Class<?> mapperClass;
	 /** Mybatis对应的mapper的xml中的sql语句集 */
	private Map<String, String> mapperSqls = new HashMap<>();
	 /** 错误信息 */
	private String errorMsg;
	 /** 主键类型 */
	private Class<?> idType;
	 /** 主键对应的实体对象的属性 */
	private String idProperty;
	 /**  逐渐对应的数据库字段 */
	private String idColumn;

	public String getErrorMsg() {
		return errorMsg;
	}

	/**
	 * @desc 构造方法,根据Mapper类名和实体类名构建对应的映射对象
	 * @param mapperClassName Mapper类名
	 * @param entityClassName 实体类名
	 */
	public EntityInfo(String mapperClassName, String entityClassName) {
		try {
			if (!StringUtils.isNull(entityClassName)) {
				entityClass = Class.forName(entityClassName);
			}

			// 通过注解获取相应信息
			if (entityClass.isAnnotationPresent(Table.class)) {
				// 通过注解获取表名
				this.tableName = entityClass.getAnnotation(Table.class).name();

				// 获取表对应的实体对象声明的属性,并处理对应的主键
				Field[] fields = entityClass.getDeclaredFields();
				for (Field field : fields) {
					// 主键
					if (field.isAnnotationPresent(javax.persistence.Id.class)) {
						// 主键的数据类型
						this.idType = field.getType();
						// 主键名称
						this.idProperty = field.getName();

						// 其他非主键属性
						Column column = field.getAnnotation(Column.class);
						if (column != null && !StringUtils.isNull(column.name())) {
							this.idColumn = column.name();
						} else {
							this.idColumn = this.idProperty;
						}
						break;
					}
				}
			} else {
				errorMsg = String.format("实体类%s没有发现关键字为[@Table]的注解对象", entityClassName);
				return;
			}

			// 增加实现mapper类的的注解功能的解析处理
			mapperClass = Class.forName(mapperClassName);
			Method[] methods = mapperClass.getDeclaredMethods();
			String sql;
			for (Method method : methods) {
				sql = null;
				if (method.isAnnotationPresent(Select.class)) {
					sql = method.getAnnotation(Select.class).value()[0];
				} else if (method.isAnnotationPresent(Update.class)) {
					sql = method.getAnnotation(Update.class).value()[0];
				} else if (method.isAnnotationPresent(Delete.class)) {
					sql = method.getAnnotation(Delete.class).value()[0];
				}
				if (sql != null) {
					String key = mapperClass.getName() + "." + method.getName();
					mapperSqls.put(key, sql);
				}
			}
		} catch (ClassNotFoundException e) {
			errorMsg = e.getMessage();
		} catch (Exception e) {
			errorMsg = String.format("解析失败,请检查类%s和%s是否存在和有效!", entityClassName, mapperClassName);
		}
	}

	/**
	 * @desc 构造方法,根据Mapper类名和实体类名和对应的表明构建对应的映射对象
	 * @param mapperClassName Mapper类名
	 * @param entityClassName 实体类名
	 * @param tableName 数据库表名
	 */
	public EntityInfo(String mapperClassName, String entityClassName, String tableName) {
		this.tableName = tableName;
		try {
			if (!StringUtils.isNull(entityClassName)) {
				entityClass = Class.forName(entityClassName);
			}

			// 通过注解获取表名
			if (StringUtils.isNull(this.tableName)) {
				this.tableName = entityClass.getAnnotation(Table.class).name();
			}
			mapperClass = Class.forName(mapperClassName);
		} catch (Exception e) {
			try {
				// 根据mapper接口解析entity Class
				Type[] types = mapperClass.getGenericInterfaces();
				Type[] tempTypes = ((ParameterizedType) types[0]).getActualTypeArguments();
				Class<?> clazz = (Class<?>) tempTypes[0];
				if (clazz != null) {
					entityClass = clazz;
				}
			} catch (Exception e1) {
			}
		}
	}

	public String getTableName() {
		return tableName;
	}

	public void setTableName(String tableName) {
		this.tableName = tableName;
	}

	public Class<?> getEntityClass() {
		return entityClass;
	}

	public void setEntityClass(Class<?> entityClass) {
		this.entityClass = entityClass;
	}

	public Class<?> getMapperClass() {
		return mapperClass;
	}

	public void setMapperClass(Class<?> mapperClass) {
		this.mapperClass = mapperClass;
	}

	public void addSql(String id, String sql) {
		mapperSqls.put(mapperClass.getName() + "." + id, sql);
	}

	public Map<String, String> getMapperSqls() {
		return mapperSqls;
	}

	public Class<?> getIdType() {
		return idType;
	}

	public String getIdProperty() {
		return idProperty;
	}

	public String getIdColumn() {
		return idColumn;
	}
}